{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>第5章 面向对象编程基础</center>\n",
    "\n",
    "<br>\n",
    "\n",
    "  - [5.1 面向对象的概念与特征](#5.1-面向对象的概念与特征)\n",
    "    - [5.1.1 面向对象的概念](#5.1.1-面向对象的概念)\n",
    "    - [5.1.2 类与对象](#5.1.2-类与对象)\n",
    "    - [5.1.3 封装性](#5.1.3-封装性)\n",
    "    - [5.1.4 继承性](#5.1.4-继承性)\n",
    "    - [5.1.5 多态性](#5.1.5-多态性)\n",
    "  - [5.2 类的定义与实例化](#5.2-类的定义与实例化)\n",
    "    - [5.2.1 类的定义](#5.2.1-类的定义)\n",
    "    - [5.2.2 类的实例化](#5.2.2--类的实例化)\n",
    "    - [5.2.3 类成员的隐藏](#5.2.3-类成员的隐藏)\n",
    "    - [5.2.4 类命名空间 *](#5.2.4-类命名空间-*)\n",
    "  - [5.3 进一步了解属性](#5.3-进一步了解属性)\n",
    "    - [5.3.1 类属性与实例属性](#5.3.1-类属性与实例属性)\n",
    "    - [5.3.2 `property`装饰器](#5.3.2--property装饰器)\n",
    "  - [5.4 进一步了解方法](#5.4-进一步了解方法)\n",
    "    - [5.4.1 实例方法、类方法与静态方法](#5.4.1-实例方法、类方法与静态方法)\n",
    "    - [5.4.2 方法重载*](#5.4.2-方法重载*)\n",
    "  - [5.5 类的继承](#5.5-类的继承)\n",
    "    - [5.5.1 派生类的定义](#5.5.1-派生类的定义)\n",
    "    - [5.5.2 方法的重写](#5.5.2-方法的重写)\n",
    "    - [5.5.3 多重继承*](#5.5.3-多重继承*)\n",
    "    - [5.5.4 对象、类的关系](#5.5.4-对象、类的关系)\n",
    "    - [5.5.5 调用基类方法](#5.5.5-调用基类方法)\n",
    "  - [5.6 混入*](#5.6-混入*)\n",
    "    - [5.6.1 混入的概念](#5.6.1-混入的概念)\n",
    "    - [5.6.2 Python 中的混入](#5.6.2-Python-中的混入)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 5.1 面向对象的概念与特征\n",
    "\n",
    "### 5.1.1 面向对象的概念\n",
    "\n",
    "-  面向对象\n",
    "  - 对象是程序的基本单元，程序是各种即有独立性又相互调用的对象构成\n",
    "\n",
    "- 编程范式\n",
    "  - 过程式\n",
    "    - 将程序看作一系列指令和函数的集合，根据一定的流程控制顺序来执行程序指令\n",
    "    - 与面向对象相容\n",
    "  - 函数式\n",
    "    - 将待解决问题解构成一系列函数\n",
    "    - 与面向对象不相容\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 问题解构的角度来看\n",
    "  - 面向对象编程是对现实世界的模拟，对象就是现实世界中的个体\n",
    "  - 每个对象都具有自已的属性，还能够作出各种行为，对象之间还能相互作用\n",
    "  - 例如，一个城市的交通系统中有很多的行人、车辆、信号灯等构成\n",
    "- 从程序设计的角度来看\n",
    "  - 对象其实是一些“变量”和一些“函数”的组合体，是为了实现特定的程序功能不可或缺的组成部分\n",
    "  - “变量”称为__属性__\n",
    "  - “函数”称为__方法__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 面向对象编程的优点\n",
    "  - 模块化程度高\n",
    "    - 能够从更加抽象的层次去解构问题、分解任务\n",
    "  - 便于代码重用\n",
    "    - 够方便地利用一种对象的功能实现另一种类型的对象\n",
    "    - __继承性__\n",
    "  - 封装性良好\n",
    "    - 属性被封装到对象之中，对象在被调用时其内部实现过程对于调用者来说是透明的\n",
    "    - __封装性__\n",
    "  - 灵活性高\n",
    "    - 不同的类形的对象是能够相互替代的，对象能够以不同的身份参与到不同的功能调用之中\n",
    "    - __多态性__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 面向对象编程的现状\n",
    "  - Simula：第一种具有面向对象特征的编程语言是\n",
    "  - Smalltalk：一种完全面向对象的编程语言\n",
    "  - 面向对象的思想影响了一大批编程语言"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.1.2 类与对象\n",
    "\n",
    "- 抽象\n",
    "  - 从一类事物中归纳、提取出它们共同的、本质的特征，忽略与关注目标无关的、非本质特征，最终形成一个能够用于描述这类事物的概念\n",
    "- 类\n",
    "  - 抽象出的概念称为类\n",
    "  - 具有普遍性、抽象性\n",
    "- 对象\n",
    "  - 对象是类的实例\n",
    "  - 个性化、具体的"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 例\n",
    "\n",
    "定义类：\n",
    "```\n",
    "类 汽车：\n",
    "    属性 车长;\n",
    "    属性 车宽;\n",
    "    方法 行驶(速度);\n",
    "```\n",
    "实例化并调用：\n",
    "```\n",
    "大黄峰是一辆汽车\n",
    "大黄峰的宽1.9米，长4.2米\n",
    "大黄峰.行驶(速度200)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.1.3 封装性\n",
    "\n",
    "- 封装性的含义\n",
    "  - 完成一类对象功能所需的数据及对数据的操作方法被打包为一个整体\n",
    "  - 对外隐藏不必要的细节\n",
    "  - 类成员的访问具有一定的权力限制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.1.4 继承性\n",
    "\n",
    "- 继承（Inheritance）\n",
    "  - 是一种创建新类的方法，能够有效地实现代码重用\n",
    "- 基类（父类）与派生类（子类）\n",
    "\n",
    "- 例\n",
    "\n",
    "```\n",
    "类 客车 继承自 汽车：\n",
    "    属性 最大载客量;\n",
    "    方法 上下客(客人数量);\n",
    "\n",
    "类 货车 继承自 汽车：\n",
    "    属性 最大载重量;\n",
    "    方法 装卸货物(货物数量);\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.1.5 多态性\n",
    "\n",
    "- 特征\n",
    "  - 派生类对象由于具有父类对象的所有功能，因而可以作为父类对象使用\n",
    "    - 例如，`汽车`类的对象能够行驶在机动车道上，`客车`类是`汽车`类的子类，其对象也能够行驶在机动车道上\n",
    "  - 在被调用时每个对象可能以不同的方式做出响应\n",
    "    - 例如，跑车的行驶与普通汽车有着显著的差异，因此`跑车`类需要重新定义`行驶`方法\n",
    "    - 相同的指令，`汽车`对象与`跑车`对象会调用各自的方法做出不同的响应"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 注意\n",
    "  - 子类对象不会因为被当作父类对象使用就变成了父类对象\n",
    "    - `客车`虽然可以被当作`汽车`来使用，但是它本质上是`客车`，不会变成普通`汽车`\n",
    "  - 子类对象被作为父类对象使用时，只能调用父类对象拥有的属性\n",
    "    - 当`客车`对象被作为`汽车`使用时，我们无法确定它究竟是什么，它可能是`货车`也可能是`跑车`，因而不能访问`最大载客量`变量或调用`上下客`方法\n",
    "  - 子类重写了父类方法时，即使子类对象被当作父类对象使用，调用的也是子类中的方法\n",
    "    - 一个`跑车`对象不论被看作`汽车`还是`跑车`，当`行驶`时被调用的只能是`跑车`中的`行驶`方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "##  5.2 类的定义与实例化\n",
    "\n",
    "### 5.2.1 类的定义\n",
    "\n",
    "```python\n",
    "class 类名:\n",
    "    '''文档字符串'''\n",
    "    类体\n",
    "```\n",
    "- 类名\n",
    "  - 合法的标识符\n",
    "- 类体\n",
    "  - 文档字符串\n",
    "  - 属性定义\n",
    "  - 方法定义\n",
    "  - 任何合法的Python代码片段"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Auto:\n",
    "    width = 0\n",
    "    length = 0\n",
    "    def run(self, speed):\n",
    "        print(f\"以速度{speed}行驶！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 定义属性和方法\n",
    "  - 属性：与普通变量定义一致\n",
    "  - 方法\n",
    "  \n",
    "   ```python\n",
    "   def 方法名(self, 参数1, 参数2, ...):\n",
    "       '''文档字符串'''\n",
    "       方法体\n",
    "   \n",
    "   ```\n",
    "  - `self`\n",
    "    - 对象自身\n",
    "    - 通过`self`来访问对象中包含的其他属性或方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Auto:\n",
    "    width = 0\n",
    "    length = 0\n",
    "    def run(self, speed):\n",
    "        print(f\"本车长{self.width}，宽{self.length}，以速度{speed}行驶！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `self`参数的名称可以任意"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.2.2  类的实例化\n",
    "\n",
    "- 实例化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车长1.5，宽3.3，以速度80行驶！\n"
     ]
    }
   ],
   "source": [
    "auto = Auto()  # 实例化\n",
    "auto.width = 1.5\n",
    "auto.length = 3.3\n",
    "auto.run(80)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 实例化的过程\n",
    "  - 由两个特殊方法`__new__`和`__init__`实现\n",
    "    - 如果用户没有定义`__new__`或`__init__`默认会调用父类的`__new__`或`__init__`方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Test:\n",
    "    def __init__(self):\n",
    "        print(f'{id(self)} in __init__')\n",
    "    \n",
    "    def __new__(cls):           # 类方法\n",
    "        o = object.__new__(cls)\n",
    "        print(f'{id(o)} in __new__')\n",
    "        return o"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "140232540383456 in __new__\n",
      "140232540383456 in __init__\n"
     ]
    }
   ],
   "source": [
    "obj = Test()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 实例化过程中参数的传递\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Test:\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        print(f'{args} in __init__')\n",
    "        print(f'{kwargs} in __init__')\n",
    "\n",
    "    def __new__(cls, *args, **kwargs):\n",
    "        o = object.__new__(cls)\n",
    "        print(f'{args} in __new__')\n",
    "        print(f'{kwargs} in __new__')\n",
    "        return o"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1,) in __new__\n",
      "{'x': 2} in __new__\n",
      "(1,) in __init__\n",
      "{'x': 2} in __init__\n"
     ]
    }
   ],
   "source": [
    "obj = Test(1, x=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Auto:\n",
    "    def __init__(self, width, length):\n",
    "        self.width = width\n",
    "        self.length = length\n",
    "    def run(self, speed):\n",
    "        print(f\"本车长{self.width}，宽{self.length}，以速度{speed}行驶！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车长1.5，宽3.3，以速度80行驶！\n"
     ]
    }
   ],
   "source": [
    "auto = Auto(1.5, 3.3)\n",
    "auto.run(80)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.2.3 类成员的隐藏\n",
    "\n",
    "- 所有名称以`__`开头的属性为隐藏成员"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TestHidden:\n",
    "    def __init__(self):\n",
    "        self.__hidden = 1\n",
    "    def out(self):\n",
    "        print(f'the hidden value is {self.__hidden}')\n",
    "    def __hidden_value(self):\n",
    "        return self.__hidden"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the hidden value is 1\n"
     ]
    }
   ],
   "source": [
    "th = TestHidden()\n",
    "th.out()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'TestHidden' object has no attribute '__hidden'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-12-1a5cd90a4d1d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mth\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__hidden\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'TestHidden' object has no attribute '__hidden'"
     ]
    }
   ],
   "source": [
    "th.__hidden"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'TestHidden' object has no attribute '__hidden_value'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-13-79203ffd8557>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mth\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__hidden_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'TestHidden' object has no attribute '__hidden_value'"
     ]
    }
   ],
   "source": [
    "th.__hidden_value()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- Python中没有真正的“私有”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "th._TestHidden__hidden"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "th._TestHidden__hidden_value()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 一种约定\n",
    "  - 以`_`开头的为隐藏成员"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.2.4 类命名空间 *\n",
    "\n",
    "- Python中的命名空间\n",
    "  - 内建命名空间：Python解释器启动时创建的命名空间；\n",
    "  - 全局命名空间：加载模块时创建的命名空间；\n",
    "  - 闭包命名空间：定义闭包时创建的命名空间；\n",
    "  - 局部命名空间：定义函数或类时创建的命名空间。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(globals())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__name__': '__main__',\n",
       " '__doc__': 'Automatically created module for IPython interactive environment',\n",
       " '__package__': None,\n",
       " '__loader__': None,\n",
       " '__spec__': None,\n",
       " '__builtin__': <module 'builtins' (built-in)>,\n",
       " '__builtins__': <module 'builtins' (built-in)>,\n",
       " '_ih': ['', 'globals()'],\n",
       " '_oh': {},\n",
       " '_dh': ['/Users/liuchen/Documents/GitHub/python_book/notebook_slides'],\n",
       " 'In': ['', 'globals()'],\n",
       " 'Out': {},\n",
       " 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7fc4d818fd00>>,\n",
       " 'exit': <IPython.core.autocall.ZMQExitAutocall at 0x7fc4d81fe580>,\n",
       " 'quit': <IPython.core.autocall.ZMQExitAutocall at 0x7fc4d81fe580>,\n",
       " '_': '',\n",
       " '__': '',\n",
       " '___': '',\n",
       " 'json': <module 'json' from '/Users/liuchen/anaconda3/envs/lesson/lib/python3.8/json/__init__.py'>,\n",
       " 'autopep8': <module 'autopep8' from '/Users/liuchen/anaconda3/envs/lesson/lib/python3.8/site-packages/autopep8.py'>,\n",
       " '_i': '',\n",
       " '_ii': '',\n",
       " '_iii': '',\n",
       " '_i1': 'globals()'}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "globals()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 类命名空间\n",
    "  - 当定义一个类之后，就产生了一个命名空间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'__module__': '__main__', '__qualname__': 'TestNamespace', 'value_test': 1, 'method_test': <function TestNamespace.method_test at 0x7f8a6ecb6670>}\n"
     ]
    }
   ],
   "source": [
    "class TestNamespace:\n",
    "    value_test = 1\n",
    "    def method_test(self):\n",
    "        pass\n",
    "    print(locals())\n",
    "\n",
    "def f(self, x):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 5.3 进一步了解属性\n",
    "\n",
    "### 5.3.1 类属性与实例属性\n",
    "\n",
    "- 定义在类命名空间中的属性称为类属性或静态属性\n",
    "  - 类的所有对象共享相同的类属性\n",
    "  - 类属性对于类内的方法来说类似于全局变量。当在方法中试图修改类属性的取值时，相当于重新定义了局部变量，而局部变量会覆盖掉类命名空间中的同名变量。\n",
    "- 定义在`self`对象中的属性称为实例属性\n",
    "  - 每个对象自有"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Test1:\n",
    "    x = [0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1] [1]\n"
     ]
    }
   ],
   "source": [
    "t11 = Test1()\n",
    "t12 = Test1()\n",
    "t11.x[0] = 1\n",
    "print(t11.x, t12.x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Test2:\n",
    "    def __init__(self):\n",
    "        self.x = [0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1] [0]\n"
     ]
    }
   ],
   "source": [
    "t21 = Test2()\n",
    "t22 = Test2()\n",
    "t21.x[0] = 1\n",
    "print(t21.x, t22.x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.3.2  `property`装饰器\n",
    "\n",
    "- 直接访问类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto1:\n",
    "    def __init__(self):\n",
    "        self.oil = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m1 = Moto1()\n",
    "m1.oil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 间接访问类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto2:\n",
    "    def __init__(self):\n",
    "        self.__oil = 0\n",
    "    \n",
    "    def get_oil(self):\n",
    "        return self.__oil\n",
    "    \n",
    "    def set_oil(self, oil):\n",
    "        self.__oil = oil    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m2 = Moto2()\n",
    "m2.get_oil()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- `property`装饰器能够将方法转换为属性的形式来使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self):\n",
    "        self.__oil = 0\n",
    "    \n",
    "    @property\n",
    "    def oil(self):\n",
    "        return self.__oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "moto = Moto()\n",
    "moto.oil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 完整的`property`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self):\n",
    "        self.__oil = 0\n",
    "    \n",
    "    @property\n",
    "    def oil(self):\n",
    "        return self.__oil\n",
    "    \n",
    "    @oil.setter\n",
    "    def oil(self, oil):\n",
    "        self.__oil = oil\n",
    "    \n",
    "    @oil.deleter\n",
    "    def oil(self):\n",
    "        del self.__oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "moto = Moto()\n",
    "moto.oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "moto.oil = 100\n",
    "moto.oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Moto' object has no attribute '_Moto__oil'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-33-88a69fe2191c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mmoto\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moil\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mmoto\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moil\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-30-4d7c99681068>\u001b[0m in \u001b[0;36moil\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0moil\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__oil\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m     \u001b[0;34m@\u001b[0m\u001b[0moil\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetter\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Moto' object has no attribute '_Moto__oil'"
     ]
    }
   ],
   "source": [
    "del moto.oil\n",
    "moto.oil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 5.4 进一步了解方法\n",
    "\n",
    "### 5.4.1 实例方法、类方法与静态方法\n",
    "#### 实例方法\n",
    "- 类中定义的普通方法第一个形参是self，表示对象自身。可以利用self来访问对象 的其他属性或方法，也可以在其他方法中使用self来访问当前方法。这类方法称为实例方法。\n",
    "\n",
    "#### 类方法\n",
    "- 用`classmethod`装饰器实现\n",
    "- 第一个参数名常为`cls`\n",
    "- 可以通过类直接调用，也可以通过对象调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TestClassMethod:\n",
    "    class_value = 0\n",
    "\n",
    "    @classmethod\n",
    "    def class_method(cls):\n",
    "        print(cls.class_value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "TestClassMethod.class_method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "tcm = TestClassMethod()\n",
    "tcm.class_method()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 静态方法\n",
    "- 用`staticmethod`装饰器实现\n",
    "- 无特殊参数\n",
    "- 可以通过类直接调用，也可以通过对象调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TestStaticMethod:\n",
    "    @staticmethod\n",
    "    def static_method():\n",
    "        print('这里是静态方法！')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这里是静态方法！\n"
     ]
    }
   ],
   "source": [
    "TestStaticMethod.static_method()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这里是静态方法！\n"
     ]
    }
   ],
   "source": [
    "tsm = TestStaticMethod()\n",
    "tsm.static_method()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.4.2 方法重载*\n",
    "- 用`singledispatchmethod`装饰器实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import singledispatchmethod\n",
    "\n",
    "class Averager:\n",
    "    @singledispatchmethod\n",
    "    def read(self, data):\n",
    "        n = len(data)\n",
    "        self.value = sum(data)/n\n",
    "\n",
    "    @read.register(dict)\n",
    "    def _(self, data):\n",
    "        data = data.values()\n",
    "        n = len(data)\n",
    "        self.value = sum(data)/n\n",
    "\n",
    "    @read.register(str)\n",
    "    def _(self, data):\n",
    "        data = [float(i) for i in data.split(',')]\n",
    "        n = len(data)\n",
    "        self.value = sum(data)/n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 5.5 类的继承\n",
    "\n",
    "### 5.5.1 派生类的定义\n",
    "\n",
    "- 语法形式\n",
    "\n",
    "```python\n",
    "class 派生类(基类名):\n",
    "    类体\n",
    "```\n",
    "\n",
    "- 默认的基类为`object`\n",
    "  - 所有的类都是`object`类的派生类\n",
    "  - `object`类中定义了很多特殊属性和特殊方法，子类重写这些方法能够对类进行定制以实现特殊的功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self, width, length):\n",
    "        self.width = width\n",
    "        self.length = length\n",
    "    \n",
    "    def run(self, speed):\n",
    "        print(f\"本车为 Moto，长{self.width}，宽{self.length}，以速度{speed}行驶！\")\n",
    "\n",
    "class Wagon(Moto):\n",
    "    pass\n",
    "\n",
    "class Coach(Moto):\n",
    "    pass\n",
    "\n",
    "class Car(Coach):\n",
    "    pass\n",
    "\n",
    "class Bus(Coach):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center> \n",
    "<img src=\"./figures/fig5-1.png\" width=\"60%\"/>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### 5.5.2 方法的重写\n",
    "\n",
    "- 在派生类中重新定义了基类中的属性或方法时，基类中的同名属性或方法会被覆盖掉，称为属性或方法的重写\n",
    "- 基类的隐藏成员在派生类中也是不可见的\n",
    "  - 基类中改变隐藏方法名称的原因"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 重写普通方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Wagon(Moto):\n",
    "    def run(self, speed):\n",
    "        print(f\"本车为 Wagon，长{self.width}，宽{self.length}，以速度{speed}行驶！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车为 Wagon，长2.2，宽10，以速度50行驶！\n"
     ]
    }
   ],
   "source": [
    "wagon = Wagon(2.2, 10)\n",
    "wagon.run(50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 重写`__init__`\n",
    "\n",
    "- 需在派生类的`__init__`方法中调用基类的`__init__`方法来初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Wagon(Moto):\n",
    "    def __init__(self, width, length, load):\n",
    "        self.carrying_load = load            # 载重量\n",
    "        Moto.__init__(self, width, length)\n",
    "\n",
    "class Coach(Moto):\n",
    "    def __init__(self, width, length, capacity):\n",
    "        self.passenger_capacity = capacity   # 载客量\n",
    "        Moto.__init__(self, width, length)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 重写`property`*\n",
    "\n",
    "- 被`property`修饰的方法以及相应的`setter`方法和`deleter`方法是一个整体\n",
    "  - 如果派生类中要重写基类中定义的`property`，不能仅重写被`property`修饰的方法，还需要将`setter`方法和`deleter`方法也重，才能保证派生类中`property`的完整性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self):\n",
    "        self.__oil = 0\n",
    "\n",
    "    @property\n",
    "    def oil(self):\n",
    "        return self.__oil\n",
    "\n",
    "    @oil.setter\n",
    "    def oil(self, oil):\n",
    "        self.__oil = oil\n",
    "\n",
    "    @oil.deleter\n",
    "    def oil(self):\n",
    "        del self.__oil\n",
    "\n",
    "class Wagon(Moto):\n",
    "    @property\n",
    "    def oil(self):\n",
    "        return super().oil    # 调用基类对象中的oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wagon = Wagon()\n",
    "wagon.oil"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "can't set attribute",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-47-9840aa297a61>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mwagon\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moil\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: can't set attribute"
     ]
    }
   ],
   "source": [
    "wagon.oil = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 重写`property`中的方法其中之一"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Wagon(Moto):\n",
    "    @Moto.oil.getter\n",
    "    def oil(self):\n",
    "        return super.oil\n",
    "    \n",
    "    @Moto.oil.setter\n",
    "    def oil(self, oil):\n",
    "        super(Wagon, Wagon).oil.__set__(self, oil)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wagon = Wagon()\n",
    "wagon.oil = 100\n",
    "wagon.oil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.5.3 多重继承*\n",
    "\n",
    "- 一个派生类继承自多个基类\n",
    "- 多重继承中会出现命名冲突问题，增加了程序的复杂性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self, width, length): \n",
    "        self.width = width\n",
    "        self.length = length\n",
    "    \n",
    "    def run(self, speed):\n",
    "        print(f\"本车为 Moto，长{self.width}，宽{self.length}，以速度{speed}行驶！\")\n",
    "\n",
    "class Wagon(Moto):\n",
    "    def __init__(self, width, length, load):\n",
    "        self.carrying_load = load            # 载重量\n",
    "        Moto.__init__(self, width, length)\n",
    "\n",
    "class Coach(Moto):\n",
    "    def __init__(self, width, length, capacity):\n",
    "        self.passenger_capacity = capacity   # 载客量\n",
    "        Moto.__init__(self, width, length)\n",
    "\n",
    "class Pickup(Wagon, Coach):\n",
    "    def __init__(self, width, length, load, capacity):\n",
    "        Wagon.__init__(self, width, length, load)\n",
    "        Coach.__init__(self, width, length, capacity)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center> \n",
    "<img src=\"./figures/fig5-2.png\" width=\"50%\"/>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 方法解析顺序（Method Resolution Order，MRO）\n",
    "  - 解释器依据该顺序进行搜索，执行找到的第一个方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(__main__.Pickup, __main__.Wagon, __main__.Coach, __main__.Moto, object)"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Pickup.__mro__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.5.4 对象、类的关系\n",
    "\n",
    "#### 关系检查\n",
    "- `isinstance`\n",
    "  - 检查一个对象是否是一个类的实例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pickup = Pickup(1.8, 3.9, 6, 2)\n",
    "isinstance(pickup, Pickup)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, True, True)"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(pickup, Wagon), isinstance(pickup, Coach), isinstance(pickup, Moto)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- `issubclass`\n",
    "  - 判断一个类是否另一个类的派生类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "issubclass(Pickup, Wagon)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "issubclass(Pickup, Coach)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "issubclass(Pickup, Moto)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 鸭子类型和协议\n",
    "\n",
    "- 动态语言特有的一种特征，其含义是对象方法要比其类型更重要\n",
    "- 为程序设计带来了很大的灵活性\n",
    "- 例：\n",
    "  - 定义一个驾驶员类`Driver`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Driver:\n",
    "    def __init__(self, moto: Moto):\n",
    "        self.moto = moto\n",
    "\n",
    "    def drive(self, speed):\n",
    "        self.moto.run(speed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车为 Moto，长1.8，宽3.9，以速度120行驶！\n"
     ]
    }
   ],
   "source": [
    "pickup = Pickup(1.8, 3.9, 6, 20)\n",
    "bobo = Driver(pickup)\n",
    "bobo.drive(120)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 蹦蹦车类`Bouncy`并不是`Moto`类的派生类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Bouncy:\n",
    "    def run(self, speed):\n",
    "        print(f\"这里一辆蹦蹦车，以速度{speed}行驶\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这里一辆蹦蹦车，以速度2行驶\n"
     ]
    }
   ],
   "source": [
    "bouncy = Bouncy()\n",
    "bobo.moto = bouncy\n",
    "bobo.drive(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `Bouncy`的实例虽然不是`Moto`的实例，但是它们的行为非常相似。这种情况被称为“鸭子类型”"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- If it walks like a duck and it quacks like a duck, then it must be a duck"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 协议\n",
    "  - 一组约定的用 于实现特定功能的方法\n",
    "  - 如果一个类中实现了一个协议中的所有方法，就可以说该类实 现了这个协议，相应地这个类的对象就具备了协议中约定的功能\n",
    "  - 协议是一种在文档层面的非正式约定，不具有强制性，在语言层面上不 会感知到协议的存在\n",
    "  - 动态语言中的协议与静态语言(如 Java 和 C++)中接口的概念具有相似的作用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### 5.5.5 调用基类方法\n",
    "\n",
    "#### 通过基类名调用基类方法\n",
    "\n",
    "- `Wagon`、`Coach`、`Pickup`等类的定义中使用了这种方法在`__init__`中调用基类的`__init__`方法来初始化基类属性\n",
    "- 缺点\n",
    "  - 增加了代码之间的耦合性\n",
    "  - 在多重继承的情况下会产生重复调用的问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self, width, length):\n",
    "        self.width = width\n",
    "        self.length = length\n",
    "        print('执行Moto类 __init__ 方法')\n",
    "\n",
    "class Wagon(Moto):\n",
    "    def __init__(self, width, length):\n",
    "        Moto.__init__(self, width, length)\n",
    "        print('执行Wagon类 __init__ 方法')\n",
    "\n",
    "class Coach(Moto):\n",
    "    def __init__(self, width, length):\n",
    "        Moto.__init__(self, width, length)\n",
    "        print('执行Coach类 __init__ 方法')\n",
    "\n",
    "class Pickup(Wagon, Coach):\n",
    "    def __init__(self, width, length):\n",
    "        Wagon.__init__(self, width, length)\n",
    "        Coach.__init__(self, width, length)\n",
    "        print('执行Pickup类 __init__ 方法')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "执行Moto类 __init__ 方法\n",
      "执行Wagon类 __init__ 方法\n",
      "执行Moto类 __init__ 方法\n",
      "执行Coach类 __init__ 方法\n",
      "执行Pickup类 __init__ 方法\n"
     ]
    }
   ],
   "source": [
    "pickup = Pickup(1.8, 3.9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 通过`super`调用基类方法\n",
    "\n",
    "- `super`\n",
    "  - 于创建代理对象或代理类的类\n",
    "  - 代理对象或代理类会按照方法解析顺序（MRO）来查找要执行的方法\n",
    "- 调用方式\n",
    "  - `super(TYPE, OBJ)`：返回一个代理对象。`TYPE`是一个类，用于指定MRO中的查找开始位置（不含`TYPE`自身），`OBJ`用于指定查找哪个类的MRO。`OBJ`必须是`TYPE`的实例，即`isinstance(OBJ, TYPE)`为`True`。\n",
    "  - `super()`：这种方式只能用于类内部，与`super(TYPE, OBJ)`一致，只不过`TYPE`和`OBJ`分别传入的是当前所属的类以及所在方法中的`self`对象。\n",
    "  - `super(TYPE1, TYPE2)`：返回一个代理类。`TYPE1`和`TYPE2`都是类，`TYPE1`用于指定MRO中的查找开始位置（不含`TYPE1`自身），`TYPE2`用于指定查找哪个类的MRO。由于返回的是代理类，所以在调用方法的时候必须考虑方法的时候必须手动传入特殊参数`self`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Moto:\n",
    "    def __init__(self, width, length):\n",
    "        self.width = width\n",
    "        self.length = length\n",
    "        print('执行Moto类 __init__ 方法')\n",
    "\n",
    "class Wagon(Moto):\n",
    "    def __init__(self, width, length):\n",
    "        super().__init__(width, length)\n",
    "        print('执行Wagon类 __init__ 方法')\n",
    "\n",
    "class Coach(Moto):\n",
    "    def __init__(self, width, length):\n",
    "        super().__init__(width, length)\n",
    "        print('执行Coach类 __init__ 方法')\n",
    "\n",
    "class Pickup(Wagon, Coach):\n",
    "    def __init__(self, width, length):\n",
    "        super(Pickup, self).__init__(width, length)\n",
    "        print('执行Pickup类 __init__ 方法')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "执行Moto类 __init__ 方法\n",
      "执行Coach类 __init__ 方法\n",
      "执行Wagon类 __init__ 方法\n",
      "执行Pickup类 __init__ 方法\n"
     ]
    }
   ],
   "source": [
    "pickup = Pickup(1.8, 3.9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 5.6 混入*\n",
    "\n",
    "\n",
    "### 5.6.1 混入的概念\n",
    "\n",
    "- 混入(MinxIn)是有别于继承的另一种代码重用机制，它从另一个角度对现实世界 中事物的关系进行组织和划分，是继承机制的一种有效的补充\n",
    "- 混入机制通常利用多重 继承机制实现，实际上混入也是多重继承最主要的应用方式\n",
    "\n",
    "#### 继承与混入的区别\n",
    "\n",
    "- 继承\n",
    "\n",
    "<center> \n",
    "<img src=\"./figures/fig5-3.png\" width=\"60%\"/>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 混入\n",
    "\n",
    "<center> \n",
    "<img src=\"./figures/fig5-4.png\" width=\"70%\"/>\n",
    "</center>\n",
    "\n",
    "- “载货能力”和“载客能力”被称为混入类，“汽 车”类称为具体类，“微卡”、“轿车”等称为被混入类\n",
    "- 混入类与被混 入类之间并不是父子关系"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 混入的优势\n",
    "  - 混入与继承相比一个重要的优势是能够有效降低代码的复杂程度\n",
    "  - 在继承关系中，继承的层次会随着新特征的加入而增加，产生冗余的代码，导致程序开发成本和维护成本增加。混入机 制就能够很好地解决这个问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 混入类应当具备如下特征\n",
    "  - 与具体类之间不存在逻辑上的依赖关系\n",
    "  - 包含了一组联系紧密的方法以实现特定的功能\n",
    "  - 不应该被实例化，混入类的对象没有存在的意义\n",
    "- 继承与混入的相同之处在于它们都包含了一组用于实现特定功能的方法;不同之处在于协议仅仅是一种不具有强制性的约定，而混入类中 包含的是具体的、已经被实现了的方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### 5.6.2 Python 中的混入\n",
    "\n",
    "#### 混入的实现方式\n",
    "\n",
    "- Python中混入是用多重继承实现的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto:                       # 具体类\n",
    "    def run(self, speed):\n",
    "        print(f\"本车为{self.__class__.__name__}，以速度{speed}行驶！\")\n",
    "\n",
    "class WagonMixIn:                 # 混入类\n",
    "    def carry_cargo(self):\n",
    "        print(\"装载货物！\")\n",
    "\n",
    "class CoachMixIn:                 # 混入类\n",
    "    def carry_passengers(self):\n",
    "        print(\"搭载乘客！\")\n",
    "\n",
    "class Truck(WagonMixIn, Moto):    # 被混入类\n",
    "    pass\n",
    "\n",
    "class Car(CoachMixIn, Moto):      # 被混入类\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车为Car，以速度80行驶！\n"
     ]
    }
   ],
   "source": [
    "car = Car()\n",
    "car.run(80)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "搭载乘客！\n"
     ]
    }
   ],
   "source": [
    "car.carry_passengers()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本车为Truck，以速度60行驶！\n"
     ]
    }
   ],
   "source": [
    "truck = Truck()\n",
    "truck.run(60)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "装载货物！\n"
     ]
    }
   ],
   "source": [
    "truck.carry_cargo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### 在混入类中调用具体类的方法\n",
    "\n",
    "- 混入类中定义的功能具有通用性，可以与多种具体类结合个性化地实现其中定义的功能\n",
    "  - 例如，`Car`和`Bus`混入`CoachMixIn`之后都具备了载客功能，但是它们的载客功能差异很大\n",
    "  - 载客功能的实现明显不是混入类`CoachMixIn`独立实现的，还需要依赖具体类`Moto`\n",
    "- 混入类中一般没有状态信息，必须依赖具体类才能真正实现功能\n",
    "  - 混入类不应当被实例化的真正原因\n",
    "- 混入类中如何调用具体类的方法?\n",
    "  - 从多重继承的语法来说，混入类和具体类之间是并列的，它们之间没有任何关系\n",
    "  - Python 的super和方法解析顺序实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "\n",
    "- `super`根据参数来确定在 MRO 中的查找方式，其参数传递形式有三种:\n",
    "  - `super(TYPE, OBJ)`:返回一个代理对象。其中TYPE是一个类，用于指定 MRO 中的 查找开始位置(不含TYPE自身)。OBJ用于指定查找哪个类的 MRO，必须是TYPE的 实例，即`isinstance(OBJ, TYPE)`必须为`True`。\n",
    "  - `super()`:这种方式只能用于类内部，与`super(TYPE, OBJ)`一致，只不过省去了显 式传入的实参`TYPE`和`OBJ`。解释器默认传入的实参是当前所属的类以及所在方法中 的`self`对象。\n",
    "  - `super(TYPE1, TYPE2)`:返回一个代理类，其中`TYPE1`和`TYPE2`都是类名，TYPE1用 于指定 MRO 中的查找开始位置(不含`TYPE1`自身)，`TYPE2`用于指定查找哪个类的 MRO。由于返回的是代理类而不是代理对象，因此在调用方法的时候必须手动传入特殊参数`self`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `super`能够调用基类的`__init__`方法的原因\n",
    "  - 以类`Wagon`为例，`super()`与`super(Wagon, self)`等价，要查找的是由`self`确定的类`Wagon`的 MRO， 从该 MRO 中类`Wagon`之后开始查找。`Wagon`之后的第一个类就是`Wagon`的基类`Moto`，因 此`super().__init__`查找到的就是`Moto`的`__init__`方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 例：在混入类中调用具体类的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Moto:                       # 具体类\n",
    "    def __init__(self, capacity):\n",
    "        self.capacity = capacity\n",
    "    def get_capacity(self):\n",
    "        return self.capacity\n",
    "\n",
    "class CoachMixIn:                 # 混入类\n",
    "    def carry_passengers(self, nums):\n",
    "        capacity = super().get_capacity()\n",
    "        if nums > capacity:\n",
    "            print(\"人员超载！\")\n",
    "        else:\n",
    "            print(f\"搭载{nums}名乘客！\")\n",
    "\n",
    "class Car(CoachMixIn, Moto):      # 被混入类\n",
    "    def __init__(self, capacity):\n",
    "        super().__init__(capacity)\n",
    "        self.capacity = capacity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "人员超载！\n"
     ]
    }
   ],
   "source": [
    "car = Car(5)\n",
    "car.carry_passengers(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "搭载3名乘客！\n"
     ]
    }
   ],
   "source": [
    "car.carry_passengers(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 基类列表中混入类必须位于具体类之前"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 例：利用混入扩展类的功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "class ReadOnlyDictMixin:\n",
    "    __slots__ = ()\n",
    "    def __setitem__(self, key, value):\n",
    "        if key in self:\n",
    "            old_value = super().__getitem__(key)\n",
    "            print(f'字典中已存在{key}值，不能修改！')\n",
    "            value = old_value\n",
    "        return super().__setitem__(key, value)\n",
    "\n",
    "class ReadOnlyDict(ReadOnlyDictMixin, defaultdict):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "字典中已存在x值，不能修改！\n"
     ]
    }
   ],
   "source": [
    "d = ReadOnlyDict()\n",
    "d['x'] = 1 \n",
    "d['x'] = 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_items([('x', 1)])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.items()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- 混入类使用原则\n",
    "  - 混入类使用特殊的命名，一般以MinxIn为后缀\n",
    "  - 混入类只应当实现功能，不应当有状态信息\n",
    "  - 不同混入类尽量不要包含名称相同的方法\n",
    "  - 在定义被混入类时，基类中可以包含多个混入类，但只应当有一个具体类，并且混入类都要位于具体类之前"
   ]
  }
 ],
 "metadata": {
  "author": "liuchen",
  "celltoolbar": "幻灯片",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  },
  "rise": {
   "enable_chalkboard": true,
   "footer": "",
   "progress": true,
   "scroll": true,
   "slideNumber": true
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "url": "https://github.com/hitlic/python_book"
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
